from sbs_shared import *

#===============================================================================
# TCPClient
#===============================================================================

class TCPClient:
    ''' Simple TCP socket wrapper '''
    
    def __init__(self): 
        self.__host = None
        self.__port = None
        self.__socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        
        self.connected = False
        
    def get_host_addr(self):
        return self.__host
        
    def connect(self, addr, port):
        self.__host = addr
        self.__port = port
        try:
            self.__socket.settimeout(5)
            self.__socket.connect( (self.__host, self.__port) )
            self.__socket.settimeout(None)
            #
            self.__socket.setblocking(False)
            self.connected = True
        except(socket.error):
            raise Exception( \
                "Could not connect to {0}:{1}".format(self.__host, self.__port))
        
    def disconnect(self):
        try:
            self.send( SBS_NETWORK_MESSAGE_DELIMITER + "0" + SBS_TCPSERVER_DELIMITER + "bye")
            self.__socket.shutdown(socket.SHUT_RDWR)
            self.__socket.close()
            self.connected = False
        except(socket.error):
            pass
        
    def send(self, data):
        try:
            self.__socket.sendall(data)
        except(socket.error):
            raise SBSNetworkFailure(
                "Couldn't send data {0} to the server".format(data) )
        
    def recv(self, size):
        try:
            return self.__socket.recv(size)
        except(socket.error):
            # If there's no data this exception will be raised.
            pass
        return None
    
#===============================================================================
# GameClient
#===============================================================================
        
#class GameClientTCPBase(TCPClient):
class GameClient(TCPClient):
    
    def __init__(self):
        TCPClient.__init__(self)

        # Sequence numbers from the clients are even numbers.
        # Sequence numbers from the server are odd numbers.
        self.__seq_no = SBS_CLIENT_SEQ_NO_START
        
        # TODO: The player ID is automatically assigned on connect.
        # Q: Do we even want this?
        self.player_id = SBS_INVALID_ID
        
        self._message_handler = {}
        
        self.verbose = True
        
    def register_message_handler(self, message, handler_fn):
        if self.verbose:
            print "register_message_handler. message:", message, "handler_fn:", handler_fn
        if not handler_fn:
            raise Exception("handler_fn == None")
        if (message == ''):
            raise Exception("message == ''")
        self._message_handler[message] = handler_fn
    
    def get_seq_no(self):
        ''' Just a way to be sure that the correct message has been received '''
        if (self.__seq_no > SBS_MAX_SEQ_NO):
            self.__seq_no = SBS_CLIENT_SEQ_NO_START
        self.__seq_no += 2
        return self.__seq_no

    def wait_for(self, seq_no = None):
        result = None
        try:   
            data = self.recv(8192)
            
            result = None
            
            if data:
                messages = data.split(SBS_NETWORK_MESSAGE_DELIMITER)[1:]
                
                for message in messages:
                    message = message.split(SBS_TCPSERVER_DELIMITER)

                    #print "server message recv:", message
                    
                    if ( int(message[0]) == seq_no ):
                        # doesn't support multiple return values
                        # you would have to put them in a list or sth similar
                        
                        try:
                            result = eval(message[2])
                        except(NameError):
                            pass

                        continue

                    elif ( int(message[0]) == SBS_INVALID_ID ):    # error
                        print '[Client] Error received from server:', message[2:]

        except(socket.error):
            return None
        
        except:
            print "[Client] Bogus message received from the server"
            print "[Client] Error details for the message:", sys.exc_info()
            
        return result
    
    def send_synchronous_message(self, format, *args):
        seq_no = self.get_seq_no()
        try:
            message = SBS_NETWORK_MESSAGE_DELIMITER + str(seq_no) + SBS_TCPSERVER_DELIMITER + format.format( *args )
        except(IndexError):
            raise Exception("Too many parameters in {0}".format(format))
        
        self.send(message)
        
        result = None
        time_start = time.clock()
        while result == None:
            time_now = time.clock()
            if time_now - time_start > SBS_CLIENT_ALLOWED_SYNC_DELAY:
                print "Timeout. Couldn't receive answer for seq_no:", seq_no
                raise SBSNetworkTimeout()
            
            result = self.wait_for( seq_no )
        
        return result
    
    def send_synchronous_message_auto(self, function_name, *args):
        ''' TODO: Replace send_synchronous_message '''
        seq_no = self.get_seq_no()
        
        try:
            message = [str(seq_no), str(function_name)]
            for i in args:
                message.append(str(i))
            message = SBS_NETWORK_MESSAGE_DELIMITER + SBS_TCPSERVER_DELIMITER.join(message)
        except:
            print "[Client] Got an exception when sending message (", \
                message, "). Exception info:", sys.exc_info(), 'Most likely a bogus message.'
            raise
        
        self.send(message)
        
        result = None
        time_start = time.clock()
        while result == None:
            time_now = time.clock()
            if time_now - time_start > SBS_CLIENT_ALLOWED_SYNC_DELAY:
                print "Timeout. Couldn't receive answer for seq_no:", seq_no
                raise SBSNetworkTimeout()
            
            result = self.wait_for( seq_no )
        
        return result
        
        
        
        
